namespace cxxcall
{

bool cb_data::_Reg_struct_ret ()
{
#ifndef _WIN32
  if (this->rsize > 16)
    {
      this->raddr = (void *)this->iargs[this->ianum++];
      return (false);
    }
#else
  if (!(this->rsize == 1 || this->rsize == 2 ||
      this->rsize == 4 || this->rsize == 16))
    {
      this->raddr = *(void **)this->curr;
      this->curr += sizeof (uintptr_t);
      ++this->anum;
      return (false);
    }
#endif

  return (true);
}

void* cb_data::xarg (size_t size, size_t align, bool integral, bool on_stack)
{
#ifndef _WIN32
  if (!integral)
    this->curr = (this->curr + cb_data::struct_alignment (align) - 1) &
      -(intptr_t)cb_data::struct_alignment (align);

  if (!on_stack && size <= 16 &&
      this->ianum + (size + WSIZE - 1) / WSIZE <= IARG_NUM)
    {
      void *ret = &this->iargs[this->ianum];
      this->ianum += (size + WSIZE - 1) / WSIZE;
      return (ret);
    }
#else
  this->anum += cb_data::arg_size (size) / WSIZE;
#endif
  void *ret = (void *)this->curr;
  this->curr += cb_data::arg_size (size);
  return (ret);
}

#ifndef _WIN32

static inline void*
farg_ptr (cb_data& c)
{
  if (c.fptr < &c.fargs[cb_data::FARG_NUM])
    return (c.fptr++);

  c.curr += WSIZE;
  return ((void *)(c.curr - WSIZE));
}

#else

static inline void*
farg_ptr (cb_data& c)
{
  c.curr += sizeof (double);
  return (++c.anum <= FARG_NUM ? &c.fargs[this->anum - 1] :
    (void *)(c.curr - sizeof (double)));
}

#endif

float cb_data::arg_f ()
{
  return (*(float *)farg_ptr (*this));
}

double cb_data::arg_d ()
{
  return (*(double *)farg_ptr (*this));
}

#ifndef CXXCALL_LONG_DOUBLE_ALIASED

long double cb_data::arg_ld ()
{
  return (*(long double *)this->xarg (sizeof (long double),
                                      alignof (long double), true, true));
}

#endif

} // namespace cxxcall
